// deno-lint-ignore-file no-unused-vars

// ---------- Example from Microsoft Build 2018 ----------
// https://www.youtube.com/watch?v=hDACN-BGvI8
type Name = { name: string };
type Id = { id: number };
type Check = { enabled: boolean };

type LabelForType<T> = T extends string ? Name
  : T extends number ? Id
  : T extends boolean ? Check
  : null;

declare function createLabel<T extends string | number | boolean>(
  value: T,
): LabelForType<T>;

const a = createLabel("Joe");
const b = createLabel(42);
const c = createLabel(true);
const d = createLabel(Math.random() > 0.5 ? "Joe" : 42);
const e = createLabel(Math.random() > 0.5 ? false : 42);

// ---------- HTTP Result Demo ----------

// ---------- Types ----------
type HttpLoading = {
  state: "loading";
};

type HttpSuccess<T> = {
  state: "success";
  value: T;
};

type HttpError = {
  state: "error";
  cause: string;
};

type HttpResult<T> =
  | HttpLoading
  | HttpSuccess<T>
  | HttpError;

type Contact = {
  name: string;
  email: string;
  phone: {
    type: string;
    number: string;
  };
};

// ---------- Test Data ----------
const failure: HttpError = {
  state: "error",
  cause: "Nothing fetched.",
};
const success: HttpResult<Contact> = {
  state: "success",
  value: {
    email: "amy@hotmail.com",
    name: "Amy",
    phone: {
      type: "work",
      number: "888-888-8888",
    },
  },
};

// ---------- Naive Impl. ----------

function extractNaive<T>(
  result: HttpResult<T>,
): T | null {
  if (result.state === "success") {
    return result.value;
  }
  return null;
}

const EF1 = extractNaive(failure);
console.log(EF1);
const ES1 = extractNaive(success);
console.log(ES1.phone); // ERROR

// ---------- Function Overloading ----------

function extractWithOverloading<T>(result: HttpSuccess<T>): T;
function extractWithOverloading<T>(
  result: HttpError | HttpLoading,
): null;
function extractWithOverloading<T>(result: HttpResult<T>): T | null {
  if (result.state === "success") {
    return result.value;
  }
  return null;
}

const EF2 = extractWithOverloading(failure);
const ES2 = extractWithOverloading(success);
console.log(ES2.phone);

// ---------- Conditional Types ----------

type Extracted<R extends HttpResult<unknown>> = R extends
  (HttpError | HttpLoading) ? null
  : R extends HttpSuccess<infer T> ? T
  : never;

function extractWithConditionalType<R extends HttpResult<unknown>>(
  result: R,
): Extracted<R> {
  if (result.state === "success") {
    return result.value as Extracted<R>;
  }
  return null as Extracted<R>;
}

const EF3 = extractWithConditionalType(failure);
const ES3 = extractWithConditionalType(success);
console.log(ES3.phone);

// ---------- Built-In Utility Types ----------
type UUID = ReturnType<typeof crypto.randomUUID>;
const taskId: UUID = "abcd";

type ExtractParams = Parameters<typeof extractWithConditionalType>;
type Param = ExtractParams[0];
